#include "WWebsocketServer.h"
#include "Manager.h"

WWebsocketServer::WWebsocketServer()
{
    _mgr = Manager::getInstance()->getMongooseMgr();
}

WWebsocketServer::~WWebsocketServer()
{

}

bool WWebsocketServer::startWsServer(int port)
{
    if (_wsPort != -1)
    {
        Logw("WWebsocketServer::startWsServer ws server is already start port:%d", _wsPort);
        return false;
    }

    std::stringstream sstream;
    sstream  << "ws://0.0.0.0:" << port;
    _wsCbMsg.wsServer = this;
    _wsCbMsg.sslFlag = false;
    mg_connection *serverConn = mg_http_listen(_mgr, sstream.str().c_str(), WWebsocketServer::recvWsRequestMsgCb, (void *)&_wsCbMsg);
    if (!serverConn)
    {
        Logw("WWebsocketServer::startWsServer ws server start failed: %s", sstream.str().c_str());
        return false;
    }
    Logi("WWebsocketServer::startWsServer ws server start success: %s", sstream.str().c_str());
    _wsPort = port;
    return true;
}

bool WWebsocketServer::startWssServer(int port, string certPath, string keyPath)
{
    if (_wssPort != -1)
    {
        Logw("WWebsocketServer::startWsServer wss server is already start port:%d", _wsPort);
        return false;
    }

    std::stringstream sstream;
    sstream  << "wss://0.0.0.0:" << port;
    _wssCbMsg.wsServer = this;
    _wssCbMsg.sslFlag = true;
    mg_connection *serverConn = mg_http_listen(_mgr, sstream.str().c_str(), WWebsocketServer::recvWsRequestMsgCb, (void *)&_wssCbMsg);
    if (!serverConn)
    {
        Logw("WWebsocketServer::startWebsocketSslServer wss server start failed: %s", sstream.str().c_str());
        return false;
    }
    Logi("WWebsocketServer::startWebsocketSslServer wss server start success: %s", sstream.str().c_str());
    _certPath = certPath;
    _keyPath = keyPath;
    _wssPort = port;
    return true;
}

void WWebsocketServer::setWsMsgCbFun(WsCbFun fun)
{
    _wsMsgCbFun = fun;
}

void WWebsocketServer::setWsConnectCbFun(WsCbFun fun)
{
    _wsConnectCbFun = fun;
}

void WWebsocketServer::setWsCloseCbFun(WsCbFun fun)
{
    _wsCloseCbFun = fun;
}

bool WWebsocketServer::run()
{
    if (_wssPort == -1 && _wsPort == -1)
    {
        usleep(1000);
        return false;
    }

    mg_mgr_poll(_mgr, 1);
    return true;
}

bool WWebsocketServer::stop()
{
    _wssPort = -1;
    _wsPort = -1;
    _certPath.clear();
    _keyPath.clear();
    mg_mgr_free(_mgr);
    return true;
}

bool WWebsocketServer::sendMsg(shared_ptr<WsRequestMsg> wsRequestMsg, const string &msg)
{
    if (_wssPort == -1 && _wsPort == -1)
    {
        return false;
    }

    if (msg.empty())
    {
        return false;
    }

    mg_connection *conn = wsRequestMsg->wsConnection;
    mg_ws_send(conn, msg.c_str(), msg.size(), WEBSOCKET_OP_TEXT);
    return true;
}

void WWebsocketServer::recvWsRequestMsg(mg_connection *conn, int msgType, void *msgData, void *cbData)
{
    WebsocketCbMsg *cbMsg = (WebsocketCbMsg *)cbData;
    if (msgType == MG_EV_ACCEPT && cbMsg->sslFlag)
    {
        struct mg_tls_opts opts;
        opts.ca = nullptr;
        opts.cert = _certPath.c_str();
        opts.certkey = _keyPath.c_str();
        opts.ciphers = nullptr;
        opts.srvname.ptr = nullptr;
        opts.srvname.len = 0;
        Logi("WWebsocketServer::recvWsRequestMsg wss connect come id:%ld", conn->id);
        mg_tls_init(conn, &opts);
    }
    else if (msgType == MG_EV_HTTP_MSG) {
        struct mg_http_message *hm = (struct mg_http_message *) msgData;
        Logi("WWebsocketServer::recvWsRequestMsg %s request id:%ld, message: %s\n", conn->is_tls ? "https" : "http", conn->id, hm->message.ptr);
        mg_str *upgrade = mg_http_get_header(hm, "Upgrade");
        if (upgrade && (upgrade->len == strlen("websocket")) &&
                (mg_ncasecmp(upgrade->ptr, "websocket", upgrade->len) == 0)) {
            conn->label[W_VALID_CONNECT_BIT] = 1;
            mg_ws_upgrade(conn, hm, NULL);
            if (_wsConnectCbFun)
            {
                shared_ptr<WsRequestMsg> wsRequestMsg = shared_ptr<WsRequestMsg>(new WsRequestMsg());
                wsRequestMsg->wsConnection = conn;
                _wsConnectCbFun(wsRequestMsg);
            }
        } else
        {
            Logw("WWebsocketServer::recvWsRequestMsg can not upgrade to websocket");
        }
    }
    else if (msgType == MG_EV_WS_MSG)
    {
        struct mg_ws_message *wm = (struct mg_ws_message *) msgData;
        if (_wsMsgCbFun)
        {
            shared_ptr<WsRequestMsg> wsRequestMsg = shared_ptr<WsRequestMsg>(new WsRequestMsg());
            wsRequestMsg->wsConnection = conn;
            wsRequestMsg->receiveMsg.resize(wm->data.len);
            memcpy((char *)wsRequestMsg->receiveMsg.c_str(), wm->data.ptr, wm->data.len);
            _wsMsgCbFun(wsRequestMsg);
        }
        // mg_iobuf_delete(&conn->recv, conn->recv.len);
    }
    else if (msgType == MG_EV_CLOSE)
    {
        if (conn->label[W_VALID_CONNECT_BIT] != 1)
        {
            return;
        }
        Logi("WWebsocketServer::recvWsRequestMsg close ws: %ld", conn->id);
        if (_wsCloseCbFun)
        {
            shared_ptr<WsRequestMsg> wsRequestMsg = shared_ptr<WsRequestMsg>(new WsRequestMsg());
            wsRequestMsg->wsConnection = conn;
            _wsCloseCbFun(wsRequestMsg);
        }
    }
}

void WWebsocketServer::recvWsRequestMsgCb(mg_connection *conn, int msgType, void *msgData, void *cbData)
{
    WebsocketCbMsg *cbMsg = (WebsocketCbMsg *)cbData;
    cbMsg->wsServer->recvWsRequestMsg(conn, msgType, msgData, cbData);
}
